/* OpenTeddy 3d sketch modeler based on the paper by T. Igarashi et al.
*      "Teddy: A Sketching Interface for 3D Freeform Design"
*
* Copyright (c) 2004 Alex Ni
*
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
* 
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ 
// C includes
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
extern "C"
{
#include "triangle.h"
}

#include "../glfw/win32/glfw.h"

// C++ includes
#include <list>
#include <vector>
using std::list;
using std::vector;
using std::less;
using std::unary_function;
using std::pair;

#include "GTS/gts.h"

#include "otconfig.h"
#include "inflate.h"
#include "pub_infl.h"

//////////////////////////////////////////////////////////////////////////
ot_public_inflate::ot_public_inflate(double* i_points, size_t i_ptct, int i_edgetess)
{
	if (i_edgetess <= 0) i_edgetess = 4;
	if (i_edgetess % 2) i_edgetess++;

	m_pInflate = new ot_inflate();
	m_pInflate->do_triangulate(i_points, i_ptct);
	m_pInflate->generate_spine();
	m_pInflate->generate_3d_shape(i_edgetess);
}

ot_public_inflate::~ot_public_inflate()
{
	delete m_pInflate;
}

GtsFace* ot_gts_add_tri_from_existing_verts(GtsSurface* s, GtsVertex** v );

// generate a GtsSurface version of the shape
GtsSurface* ot_public_inflate::generate_gts(GtsSurface* s)
{
	GtsVertex** gts_verts = new GtsVertex * [m_pInflate->n3ptct];
	for (size_t i = 0;i < m_pInflate->n3ptct;i++)
	{
		gts_verts[i] = gts_vertex_new (s->vertex_class, m_pInflate->n3pts[i * 3], m_pInflate->n3pts[i * 3 + 1], m_pInflate->n3pts[i * 3 + 2]);
	}

	exp_tri xtri;
	exp_edge xedge;
	for (std::list<exp_tri>::iterator i = m_pInflate->n3tri.begin(); i != m_pInflate->n3tri.end(); ++i)
	{
		xtri = (*i);
		GtsVertex* vs[3];
		for (int i = 0;i < 3;i++){ vs[i] = gts_verts[xtri[i]]; }

		ot_gts_add_tri_from_existing_verts(s, vs);
	}
	delete[] gts_verts;
	gts_verts = NULL;

	if (1)
	{
		int a = gts_surface_is_closed(s);
		int b = gts_surface_is_manifold(s);
		printf("GTS Inflated... closed:  %d,   mainfold:   %d\n", a, b);
		OT_ASSERT(a);
		OT_ASSERT(b);
	}

	// all the other pointers are now in s
	return s;
}

#define NEIGH(i,j)   m_pInflate->out.neighborlist[ ((i)*3) + (j) ]
#define PT(i,j)      m_pInflate->points[ (2*(i)) + (j) ]
#define NPT(i,j)     m_pInflate->npoints[ (2*(i)) + (j) ]
#define N3PT(i,j)	 m_pInflate->n3pts[ (3*(i)) + (j) ]
#define EDGE(i,j)    m_pInflate->out.edgelist[ ((i)*2) + (j) ]
#define TRI(i,j)     m_pInflate->out.trianglelist[ (3*(i)) + (j) ]
#define NEIGHCNT(a)  (  ( (NEIGH((a),0) == -1) ? 0 : 1 )+\
						( (NEIGH((a),1) == -1) ? 0 : 1 )+\
						( (NEIGH((a),2) == -1) ? 0 : 1 )  )

// draw (in OpenGL) debugging information for the inflate
void ot_public_inflate::draw_2d_debug()
{
	if (!m_pInflate->triangulated) return ;

	glDisable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);  // the triangles should face the right way

	for (int i = 0;i < m_pInflate->out.numberoftriangles;i++)
	{
		int numneighbors = NEIGHCNT(i);
		if (numneighbors == 1)
		{
			glColor4d(0.0, 0.0, 1 ? 1.0 : 0.5, 1.0);
		}
		else if (numneighbors == 2)
		{
			glColor4d(1 ? 1.0 : 0.5, 0.0, 1 ? 1.0 : 0.5, 1.0);
		}
		else if (numneighbors == 3)
		{
			glColor4d(0.0, 1 ? 1.0 : 0.5, 0.0, 1.0);
		}

		glBegin(GL_TRIANGLES);
		glVertex2d(PT(TRI(i, 0), 0), PT(TRI(i, 0), 1));
		glVertex2d(PT(TRI(i, 1), 0), PT(TRI(i, 1), 1));
		glVertex2d(PT(TRI(i, 2), 0), PT(TRI(i, 2), 1));
		glEnd();
	}

	glColor4f(1.0, 1.0, 1.0, 0.75);
	for (std::list<exp_tri>::iterator i = m_pInflate->newtri.begin();
			i != m_pInflate->newtri.end(); ++i)
	{

		glBegin(GL_TRIANGLES);
		glVertex2d(NPT((*i)[0], 0), NPT((*i)[0], 1));
		glVertex2d(NPT((*i)[1], 0), NPT((*i)[1], 1));
		glVertex2d(NPT((*i)[2], 0), NPT((*i)[2], 1));
		glEnd();
	}



	glColor4f(1.0, 0.0, 0.0, 1.0);
	glBegin(GL_LINES);
	for (int i = 0;i < m_pInflate->out.numberofedges;i++)
	{
		glVertex2d(m_pInflate->points[2*m_pInflate->out.edgelist[2*i]],
				m_pInflate->points[2*m_pInflate->out.edgelist[2*i] + 1]);
		glVertex2d(m_pInflate->points[2*m_pInflate->out.edgelist[2*i + 1]],
				m_pInflate->points[2*m_pInflate->out.edgelist[2*i + 1] + 1]);
	}
	glEnd();

	glColor4f(0.0, 0.0, 0.0, 0.75);
	for (std::list<exp_tri>::iterator i = m_pInflate->newtri.begin();
			i != m_pInflate->newtri.end(); ++i)
	{

		glBegin(GL_LINES);
		glVertex2d(NPT((*i)[0], 0), NPT((*i)[0], 1));
		glVertex2d(NPT((*i)[1], 0), NPT((*i)[1], 1));
		glVertex2d(NPT((*i)[1], 0), NPT((*i)[1], 1));
		glVertex2d(NPT((*i)[2], 0), NPT((*i)[2], 1));
		glVertex2d(NPT((*i)[2], 0), NPT((*i)[2], 1));
		glVertex2d(NPT((*i)[0], 0), NPT((*i)[0], 1));
		glEnd();
	}

	glColor4f(1.0, 0.0, 1.0, 1.00);
	for (std::vector<exp_edge>::iterator i = m_pInflate->intext_v.begin();
			i != m_pInflate->intext_v.end(); ++i)
	{
		glBegin(GL_LINES);
		glVertex2d(NPT((*i)[0], 0), NPT((*i)[0], 1));
		glVertex2d(NPT((*i)[1], 0), NPT((*i)[1], 1));
		glEnd();
	}

	glEnable(GL_POINT_SMOOTH);
	for (int i = m_pInflate->ptct;i < m_pInflate->nptct;i++)
	{
		glPointSize(3.0);
		double dis = m_pInflate->spine_sum_dist[i] / m_pInflate->spine_sum_total[i];
		glColor4f(10.0 * dis, 0.0, 0.0, 1.00);
		for (double d = 0.0; d < 6.282; d += 0.1)
		{
			glBegin(GL_POINTS);
			glVertex2d(cos(d)*dis + NPT((i), 0), sin(d)*dis + NPT((i), 1));
			glEnd();
		}
	}
}